李伟山:金融撮合架构
The following article is from 技术方舟 Author 李伟山
李伟山
《架构宝典》联合作者
读完需要
10分钟速读仅需 4 分钟
李伟山,某司 CTO,曾负责电商平台、活动营销平台的架构设计研发工作,开发设计了一款云同步社交平台——号簿管家。曾参与设计开发亿万级流量的阿里虚拟业务平台——话费充值、网游联运平台。在分布式系统架构设计、高并发系统设计、 系统稳定性保障等领域积累了丰富的实践经验,对基于 HTTP 协议的 SOA 架构有深入研究,在排查解决线上问题和故障方面有丰富的实践经验,擅于利用数据分析解决实际问题。对新技术有浓厚的兴趣,并乐于分享。中生代技术、技术琐话坐馆老司机;
1
概述
随着信息技术的日新月异和金融业务的快速发展,金融交易领域对于核心技术的需求也在不断增强,国内外金融交易模式已经从传统人工叫价的方式变成了由高度电子化交易系统撮合订单的方式。传统的金融交易主要发生在有型金融市场中,金融交易的买卖双方通过叫价进行价格协商等方式最终达成一致,从而形成一笔交易,同时按照交易订单到指定的交割地点进行实物交割。传统的金融交易的整个过程主要依靠人来执行,其缺点主要有效率低、速度慢、交易时间限制大、交易空间限制大、交易成本非常髙、容易有内幕交易、交易扩展性差、交易容易出错、资金安全性差等一系列缺点。
通过了解熟悉电子交易市场中订单的下单流程、交易所的订单处理机制及从订单下达到交易所形成交易价格的过程,深入了解交易所撮合订单的方式与机制,从而总结出交易撮合系统的需求,对系统进行分析建模并设计系统架构,根据真实市场机制采用多层分布式体系和内存撮合的数据存储模式,构建出一个较为完善的交易撮合系统。
2
系统总体设计
2.1
系统核心模块
交易撮合系统中包括以下几个核心模块。
交易层: 用户最终可以通过终端进行撮合委托、委托查询、出入金等操作。
接口层: 将用户的商品查询、下单等请求派发给业务层,并把交易详情反馈给交易层。
业务层: 交易系统中的核心部分,用于接收订单,根据业务逻辑实现订单撮合,同时生成交易记录,随后给予用户交易结果反馈。
数据层: 用来存储商品信息、交易信息、资金信息、用户信息,并实现数据持久化,
同时对交易详情按行进行缓存,以减轻数据库的压力。此外,根据不同类型的金融交易产品将撮合模块划分为若干业务分区,每个分区独立进行撮合,彼此不受影响。对于单个业务分区而言,撮合系统的整体架构设计如图 15.1 所示。本章的总体设计围绕业务层(撮合引擎层),以及撮合引擎与接口层(网关层)、数据层(数据库)的交互方式进行。
2.2
撮合算法设计
如图 15.2 所示,撮合引擎的核心业务模块就是交易撮合算法,其可以对客户订单进行公平合理的排列和撮合,但是我们需要保证撮合算法的公平性、高效性及高扩展性等。由于不同金融交易系统的撮合业务各有不同,因此本节仅对通用的交易撮合算法进行概括性描述。
订单队列
交易撮合的重要组成部分就是买卖订单,通过对买卖订单进行撮合,最后形成交易记录,所以对无法立刻完成撮合的订单需要有买入队列和卖出队列来保存订单。队列按照“价格优先,同价格下时间优先”的原则排序。买入队列按照委托价格由高到低的顺序排列, 卖出队列则按照委托价格由低到高的顺序排列,如图 15.3 所示。
交易委托有如下四种情况。
一. 新进的订单是买入市价委托: 如果卖出队列中有市价委托,则对它们进行撮合;如果将卖出队列中的市价委托全部进行撮合后,该买入市价委托还有可交易数量,则将买入市价委托和卖出队列中的限价委托进行撮合。如果卖出队列中没有市价委托,则直接将买入市价委托和卖出队列中的限价委托进行撮合。如果卖出队列为空, 则只将该买入市价委托插入买入队列。
二. 新进的订单是买入限价委托: 如果卖出队列中有市价委托,则先对它们进行撮合; 如果市价委托全部撮合完毕后,该买入限价委托还存在可交易数量且价格大于等于卖出队列中限价委托的最低价格,则继续跟限价委托撮合。如果卖出队列中只有限价委托且该买入委托的价格大于等于卖出队列中的最低价格,则对它们进行撮合, 否则只能将该买入委托按照撮合原则插入买入队列。如果卖出队列为空,则直接将该买入限价委托按照撮合原则插入买入队列。
三. 新进的订单是卖出市价委托: 如果买入队列中有市价委托,则先对它们进行撮合; 如果撮合后该卖出队列还有未成交量,则和买入队列中限价委托撮合。如果买入队列中仅有限价委托,则和限价委托撮合。如果买入队列为空,则将该卖出市价委托插入卖出队列中。
四. 新进的订单是卖出限价委托: 如果买入队列中有市价委托,则先对它们进行撮合; 如果将市价委托全部撮合完毕后,该卖出限价委托还存在可交易数量且价格小于等于买入队列中的最高价格,则继续跟限价委托撮合。如果买入队列中只有限价委托 且该卖出限价委托的价格小于等于买入队列中的最高价格,则将其进行撮合,否则只能将该卖出限价委托按照撮合原则插入卖出队列。如果买入队列为空,则直接将该卖出限价委托按照撮合原则插入卖出队列。
撮合顺序
撮合引擎接收到新的买入订单,则会到卖出队列的头部查找是否存在符合价格规则的卖出订单,如果存在卖出价格小于或等于买入价格的订单,则从队列中取出此订单并撮合成一笔交易;如果卖出队列为空或队列头部不满足价格关系,则将买入订单插入买入队列中,由于买入队列是按照价格与时间进行排序的,所以新插入的订单会经过一次排序再插入买入队列。
相同地,当撮合引擎接收到新的卖出订单时,会到买入队列的头部查找是否存在符合价格规则的买入订单,如果存在买入价格大于或等于卖出价格的订单,则从订单队列中取出此订单并撮合成一笔交易; 如果买入队列为空或队列头部不满足价格关系,则将卖出订单插入卖出队列中,由于卖出队列也是按照价格与时间进行排序的,所以新插入的订单会经过一次排序再插入卖出队列。结合买卖订单情况,撮合算法的流程如图 15.4 所示。从图 15.4 所示的撮合顺序可知,买卖队列的有序性是保证撮合顺序的基础,并且撮合过程中的每笔订单都可以撮合出当前最优交易。
2.3
基于内存撮合
当前的数据库撮合技术性能低下的原因在于与数据库交互过多,使得 I/O 操作很多,系统整体处理速度受数据库事务逻辑约束。
这里釆用内存撮合技术,通过最大限度地去除与数据库的交互(如图 15.5 所示),将整个撮合逻辑放在内存中进行,因此比数据库撮合技术少了许多 I/O 操作,在性能上可以大幅提升撮合速度; 内存撮合的弊端就是在由于内存的易失性而使服务器出现故障停机时,所有的交易数据将会丢失,系统的可靠性及一致性都会相应降低。因此,本章在提高内存撮合技术可靠性方面采用了多机热备份及分布式一致性技术作为补充,从而获得内存撮合技术的高性能及数据库撮合技术的数据持久性。
2.4
灾备的多机设计
由于内存撮合技术在撮合引擎出现异常时的可靠性和一致性非常差,而金融交易系统因为其业务特性对服务中断及数据丢失的容忍度非常低,所以为了提高容错性,一般多采用多机热备份技术。采用多机热备份技术将一组撮合引擎部署成互为备份的撮合引擎集群, 并在同一时间内只让一台撮合引擎提供服务,当这台撮合引擎出现故障无法继续正常工作时,撮合引擎集群便会迅速检测到这个故障,并选举出一个备份撮合引擎接管故障撮合引擎的任务,从而保证整个撮合系统正常运行。多机热备份技术的本质就是针对服务器临时故障所做的一种备份技术,本章采用多机热备份技术来避免长时间的撮合服务中断,保证撮合系统长期、可靠的服务。如图 15.6 所示,通过用多台撮合引擎做热备份来保证在撮合引擎出现故障时,在可以接受的时间内完成主机和备份机之间的切换,由备份机提供无缝连续服务。
通过釆用多机热备份技术,尽可能地避免了单一内存撮合引擎发生故障时导致的系统不可用问题,但仍旧无法提供 100%的可用性,因为当大规模服务器集群发生故障时仍旧存在服务不可用的可能性,但在实际生产环境中,3 台互为备份的服务器可以提供较高的、用于生产环境的可靠性。
2.5
状态机复制
由于内存撮合技术在撮合引擎出现异常时的可靠性和一致性非常差,而金融交易系统因为其业务特性对服务中断及数据丢失的容忍度非常低,所以为了提高容错性,一般多采用多机热备份技术。采用多机热备份技术将一组撮合引擎部署成互为备份的撮合引擎集群, 并在同一时间内只让一台撮合引擎提供服务,当这台撮合引擎出现故障无法继续正常工作时,撮合引擎集群便会迅速检测到这个故障,并选举出一个备份撮合引擎接管故障撮合引擎的任务,从而保证整个撮合系统正常运行。多机热备份技术的本质就是针对服务器临时故障所做的一种备份技术,本章采用多机热备份技术来避免长时间的撮合服务中断,保证撮合系统长期、可靠的服务。如图 15.6 所示,通过用多台撮合引擎做热备份来保证在撮合引擎出现故障时,在可以接受的时间内完成主机和备份机之间的切换,由备份机提供无缝连续服务。
通过釆用多机热备份技术,尽可能地避免了单一内存撮合引擎发生故障时导致的系统不可用问题,但仍旧无法提供 100%的可用性,因为当大规模服务器集群发生故障时仍旧存在服务不可用的可能性,但在实际生产环境中,3 台互为备份的服务器可以提供较高的、 用于生产环境的可靠性。
2.6
状态机复制
由于多机热备份技术引入了多台互为热备份的撮合引擎,因此根据撮合系统设计及撮合逻辑要求,需要保证服务器之间的数据一致,这就需要保证多服务器之间的一致性,这也是该系统的难点之一。
在设计中使用一种内存状态机复制方案,即将撮合算法视作一个确定性状态机,将其复制多份并部署到撮合系统的多台撮合引擎中。每个撮合引擎副本从相同的初始状态开始运行,当撮合系统收到网关发来的订单时,系统中的每个撮合引擎都会撮合这个订单,并依次产生交易记录,同时更新确定性撮合算法状态机的独立状态。通过这样的方式,可以使每个撮合引擎副本在撮合系统正常运转时具有相同的结果状态,而在撮合系统出现故障或异常时,撮合引擎就会出现状态不一致的情况,换句话说,一旦撮合系统的结果或状态出现了不一致的情况就可以断定系统出现了异常。
一. 关键技术点系统为了实现基于内存状态机复制的撮合系统,总结出撮合系统中的几个关键技术点:
将确定性撮合算法状态机服务部署到多台独立撮合引擎中。
接收网关订单,并将其作为确定性撮合算法状态机的输入。
根据撮合算法的需求,选择一种订单排序方式。
每台撮合引擎对按照排序方式排序过的订单进行撮合。
将确定性撮合算法状态机输出的交易记录作为给用户或数据库的响应。
监控撮合引擎副本的状态或输出的差别。
二. 具体实现为了实现基于内存状态机复制的撮合系统,系统主要通过以下方案实现状态机复制的关键技术点:
采用原子多播解决撮合引擎订单的可靠多播与全局有序性。
采用基于无锁订单队列的流水线撮合技术提供快速的订单撮合。
采用异步一致性持久化技术实现与数据库的交互。
采用失效备援技术对撮合引擎集群进行状态监控并保证系统的容错能力。
采用进度追赶技术解决撮合引擎的故障以及添加新的撮合引擎。
三. 数据库优化撮合系统中对数据库的查询操作是很重要的一环,查询的性能对是否能够拥有高效的撮合效率有很大影响。为此,对数据库进行查询优化必不可少。每个查询都会有许多可供选择的执行策略和操作算法,查询优化就是选择一个高效执行的查询处理策略。查询的执行开销主要包括 I/O 开销(磁盘存取块数)、CPU 开销(处理机时间)、内存开销,其中 I/O 开销是最主要的。为此,可以充分利用索引的方法来减少表扫描的 I/O 开销,尽量避免表搜索的发生,例如可以在订单表中对 orderID 或 itemID 建立索引以提高查询效率。利用好索引可以极大地提高数据库的检索性能,但是不能基于经常修改的列建立索引,因为这样只会增加系统搜索的开销。而且由于撮合系统每秒钟都需要处理大量的数据,因此为了提高系统效率,可以尽量建立单索引提高查询效率。(未完待续)
本文节选自中生代技术社区丛书之《架构宝典》
往期推荐